<?php
/**
 * 高德地图封装基类
 * User: zucheng
 * Date: 2019/4/12
 */

namespace App\Library;

use App\Traits\HasHttpRequest;

class AmapManage
{
    use HasHttpRequest;

    protected $config;
    protected $baseUri = 'https://restapi.amap.com';

    public function __construct($config)
    {
        $this->config = $config;
    }

    /**
     * 坐标转换
     * @param string $locations 经度和纬度用","分割，经度在前，纬度在后，经纬度小数点后不得超过6位。多个坐标对之间用”|”进行分隔最多支持40对坐标。
     * @param string $coordSys 输入坐标系。可选值： gps、mapbar、baidu、autonavi(不进行转换)
     * @return bool|array
     */
    public function coordinateConvert($locations, $coordSys)
    {
        $parameters = ['locations' => $locations, 'coordsys' => $coordSys];
        $response   = $this->get('/v3/assistant/coordinate/convert', $this->getParameters($parameters));
        if (is_object($response) && $response->status == 1 && $response->locations) {
            return array_shift($response->locations);
        }
        return false;
    }

    /**
     * 地理编码
     * @param string $address 结构化地址信息
     * @param string $city 指定查询的城市
     * @param string $callback 回调函数
     * @return object|false
     */
    public function geo($address, $city = null, $callback = '')
    {
        $parameters = ['address' => $address, 'batch' => 'false'];
        if ($city) $parameters['city'] = $city;
        if ($callback) $parameters['callback'] = $callback;
        $response = $this->get('/v3/geocode/geo', $this->getParameters($parameters));
        if (is_object($response) && $response->status == 1 && $response->geocodes) {
            return array_shift($response->geocodes);
        }
        return false;
    }

    /**
     * 逆地理位置编码 接收 GCJ02 坐标
     * @param float $location 经纬度 经度在前,纬度在后
     * @param string $extensions base|all 基础数据和全部数据
     * @return object|false
     */
    public function regeo($location, $extensions = 'base')
    {
        $parameters = ['location' => $location, 'extensions' => $extensions];
        $response   = $this->get('/v3/geocode/regeo', $this->getParameters($parameters));
        if (is_object($response) && $response->status == 1) {
            if ($response->regeocode->addressComponent) {
                return $response->regeocode->addressComponent;
            }
        }
        return false;
    }

    /**
     * 行政区查询
     * @param string $keywords 规则：只支持单个关键词语搜索关键词支持：行政区名称、citycode、adcode 例如，在subdistrict=2，搜索省份（例如山东），能够显示市（例如济南），区（例如历下区）
     * @param int $subdistrict 子级行政区 级数 范围：0~3
     * @param string $extensions 返回结果控制 all|base
     * @return bool|mixed
     */
    public function district($keywords, $subdistrict = 1, $extensions = 'base')
    {
        $parameters = ['keywords' => $keywords, 'subdistrict' => $subdistrict, 'extensions' => $extensions, 'offset' => 200];
        $response   = $this->get('/v3/config/district', $this->getParameters($parameters));
        if (is_object($response) && $response->status == 1 && $response->districts) {
            return $response->districts;
        }
        return false;
    }

    /**
     * 货车路径规划
     * doc: https://lbs.amap.com/api/webservice/guide/api/direction
     * @param $srcLocation 出发地经纬度
     * @param $dstLocation 目的地经纬度
     * @param int $size 车辆大小(国际分类) 1：微型车，2：轻型车（默认值），3：中型车，4：重型车
     * @param int $strategy 策略：8高度优先 9躲避拥堵&高速优先
     * @param string $extensions
     * @return bool
     */
    public function directionByTruck($srcLocation, $dstLocation, $size = 2, $strategy = 8, $extensions = 'base')
    {
        $parameters = ['origin' => $srcLocation, 'destination' => $dstLocation, 'size' => $size, 'extensions' => $extensions, 'strategy' => $strategy];
        $response   = $this->get('/v4/direction/truck', $this->getParameters($parameters));
        if (is_object($response) && $response->errcode == 0) {
            return $response->data->route->paths;
        }
        return false;
    }

    /**
     * 距离测量
     * doc: https://lbs.amap.com/api/webservice/guide/api/direction
     * @param $srcLocation 出发地经纬度
     * @param $dstLocation 目的地经纬度
     * @param int $type 策略：0直线距离 1驾车导航距离(躲避拥堵的路线) 2公交规划距离(同城坐标) 3步行规划距离(5km范围内的距离)
     * @return bool
     */
    public function distance($srcLocation, $dstLocation, $type = 0)
    {
        $parameters = ['origins' => $srcLocation, 'destination' => $dstLocation, 'type' => $type];
        $response   = $this->get('/v3/distance', $this->getParameters($parameters));
        if (is_object($response) && $response->status == 1) {
            // distance 单位：米
            return $response->results;
        }
        return false;
    }

    /**
     * 计算两点地理坐标之间的距离
     * @param  Decimal $longitude1 起点经度
     * @param  Decimal $latitude1 起点纬度
     * @param  Decimal $longitude2 终点经度
     * @param  Decimal $latitude2 终点纬度
     * @param  Int $unit 单位 1:米 2:公里
     * @param  Int $decimal 精度 保留小数位数
     * @return Decimal
     */
    public function getLinearDistance($longitude1, $latitude1, $longitude2, $latitude2, $unit = 2, $decimal = 1)
    {
        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI           = 3.1415926;

        $radLat1 = $latitude1 * $PI / 180.0;
        $radLat2 = $latitude2 * $PI / 180.0;

        $radLng1 = $longitude1 * $PI / 180.0;
        $radLng2 = $longitude2 * $PI / 180.0;

        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;

        $distance = 2 * asin(sqrt(pow(sin($a / 2), 2) + cos($radLat1) * cos($radLat2) * pow(sin($b / 2), 2)));
        $distance = $distance * $EARTH_RADIUS * 1000;

        if ($unit == 2) {
            $distance = $distance / 1000;
        }

        return round($distance, $decimal);

    }

    /**
     * 获取的接口地址
     * @param $url
     * @return string
     */
    private function setUri($url)
    {
        return $this->baseUri . '/' . ltrim($url, '/');
    }

    /**
     * 生成最后的请求参数
     * @param array $parameters
     * @return array
     */
    private function getParameters(array $parameters)
    {
        $parameters['key'] = $this->config['web_app_key'];
        ksort($parameters);

        $str = '';
        foreach ($parameters as $key => $value) {
            $str .= "&$key=$value";
        }
        $str = trim($str, "&") . $this->config['private_key'];

        $parameters['sign'] = md5($str);

        return $parameters;
    }

}